/**
 * @fileoverview This file contains the odfFileConverter object. This object provides functions for loading a
 *               stylesheet, loading an odf file by retrieving the relevant XML files and then combining these
 *               into 1 XML file, and also for transforming the combined XML file into HTML based on an XSLT stylesheet.
 *
 * Some of the codes used were based on the following existing addons for Firefox.
 * 1. ODFReader 0.2.1 (Updated October 24, 2006)
 *     https://addons.mozilla.org/en-US/firefox/addon/1888
 *    @author Talin(C) 2005-6
 *    @author Alex Hudson (C) 2006
 *    Released under the MPL/LGPL/GPL tri-license
 * 2. OdfPlugin 0.17b (Updated October 23, 2007)
 *     https://addons.mozilla.org/en-US/firefox/addon/5907
 *    @author David Staessens (02-05-07)
 * - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - =
 *
 * @author last modified by: Jamie L. Vargas (2009/08/03)
 * Released under GPLv3. See file gpl-3.0.txt included in this package.
 */

/**
 * @constructor
 */
function odfFileConverter() { }

/**
 * Uses an XSL StyleSheet to transform an ODF File.
 *
 * @param {string} aUri The XSL file to be used for the transformation.
 * @return {nsIDOMDocument} The loaded XML file.
 */
odfFileConverter.loadXmlDocument = function(aUri) {
    logDebug("odfFileConverter::loadXmlDocument", "----START----");

    var load = Cc[ "@mozilla.org/xmlextras/xmlhttprequest;1" ].createInstance(Ci.nsIXMLHttpRequest);

    try {
        load.open("GET", aUri, false);
        load.overrideMimeType("text/xml");
        load.send(null);
        logDebug("odfFileConverter::loadXmlDocument", "XML file " + aUri + " was loaded.");
    } catch (error) {
        logDebug("odfFileConverter::loadXmlDocument", "Couldn't open " + aUri);
        throw error; // No need to log actual error here since it will also be logged in catch of calling method (transform)
    }

    logDebug("odfFileConverter::loadXmlDocument", "----END----");
    return load.responseXML;
}

/**
 * Retrieves the appropriate stylesheet based on the input file's mimetype.
 *
 * @param {nsIFile} aInputFile The archive file (temporary file representing the original ODF file) to be transformed.
 * @return {nsIDOMDocument} The loaded XML file for the stylesheet (XSL).
 */
odfFileConverter.getStylesheet = function(aInputFile) {
    logDebug("odfFileConverter::getStylesheet", "----START----");

    // invoke an instance of the zipReader XPCom object
    var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader);
    logDebug("odfFileConverter::getStylesheet", "Zip reader instance created to retrieve the document's mimetype.");

    // attach our zipfile to the zipReader object and open it
    try {
        zipReader.open(aInputFile);
    } catch (e) {
        // for versions lower than 3.0; use different way of opening the file
        zipReader.init(aInputFile);
        zipReader.open();
    }

    //---------------start v0.2.0-ADD
      //extract images to the temp folder
      const tempDir = gTempDir;
      var entries = zipReader.findEntries("Pictures/*");
      try {
          while (entries.hasMore()) {
              odfFileConverter.extractPics(entries, zipReader, tempDir);
            }
      } catch (e) {
          logDebug("odfFileConverter::getStylesheet", e);
          // for versions lower than 3.0; zipfile entries are read differently
          while (entries.hasMoreElements()) {
              odfFileConverter.extractPics(entries, zipReader, tempDir);
          }
      }
    //---------------end v0.2.0-ADD

    // retrieve the document's mimetype
    var mimetypeEntry = zipReader.getEntry("mimetype");
    var mimetypeStream = zipReader.getInputStream("mimetype");
    zipReader.close();

    // we need to create a scriptablestream on our stream, so we can use it
    var scriptablestream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
    scriptablestream.init(mimetypeStream);

    var mimetype = scriptablestream.read(mimetypeEntry.realSize);
    logDebug("odfFileConverter::getStylesheet", "Document's mimetype is " + mimetype);

    var stylesheet;
    for ( var i in ODF_TYPES) {
        if (mimetype == ODF_TYPES[i][0]) {
            stylesheet = ODF_TYPES[i][2];
            break;
        }
    }
    logDebug("odfFileConverter::getStylesheet", "Appropriate document stylesheet selected.");

    logDebug("odfFileConverter::getStylesheet", "----END----");
    return this.loadXmlDocument(stylesheet);
}

/**
 * Extracts the entry item into the Pictures directory under the machine's Temporary Directory.
 * (function added in v0.2.0)
 *
 * @param {nsIUTF8StringEnumerator} entries The entries of the archive file.
 * @return {nsIZipReader} zipReader The reader for the archive file.
 * @return {nsIFile} tempDir The location to extract the pictures into.
 */
odfFileConverter.extractPics = function(entries, zipReader, tempDir) {
    logDebug("odfFileConverter::extractPics", "----START----");

    var imageName = entries.getNext(); //iterate the files in the pictures folder
    //sometimes empty entries are discovered (imageName="pictures/") so check if imageName > 9
    if ((imageName.length > 9)) {
      var unzipFile = tempDir.clone();
      unzipFile.append("Pictures");
      if (!unzipFile.exists()) {
          try {
              unzipFile.create(Ci.nsILocalFile.DIRECTORY_TYPE, 0777);
          } catch (e) {
              logDebug("odfFileConverter:extractPics", "error in creating target file for extraction \n " +
                    " file = " + unzipFile.path + ", exception = " + e + "\n");
          }
      }
      unzipFile.append(imageName.substring(imageName.lastIndexOf('/') + 1, imageName.length));
      if (unzipFile.exists()) { unzipFile.remove(false); }
      zipReader.extract(imageName, unzipFile);
      logDebug("odfFileConverter::extractPics", "Extracted image: " + imageName);
    }

    logDebug("odfFileConverter::extractPics", "----END----");
}

/**
 * Transform an ODF File to HTML for display in the browser.
 *
 * @param {nsIFile} aInputFile The archive file (temporary file representing the original ODF file) to be transformed.
 * @return {string} serializedODFData The transformed data.
 */
odfFileConverter.transform = function(aInputFile, aFileName) {
    logDebug("odfFileConverter::transform", "----START----");

    logDebug("odfFileConverter::transform", "Opening odf file to retrieve the content, styles and meta XML files.");

    // Get the relevant xml files from the OpenDocument
    var baseUri = "jar:file://" + aInputFile.path + "!/";

    var doc_content = this.loadXmlDocument(baseUri + 'content.xml');
    var doc_style = this.loadXmlDocument(baseUri + 'styles.xml');
    var doc_meta = this.loadXmlDocument(baseUri + 'meta.xml');
    var doc_settings = this.loadXmlDocument(baseUri + 'settings.xml');

    var masterDoc = Cc["@mozilla.org/xul/xul-document;1"].createInstance( Ci.nsIDOMDocument );
    var root = masterDoc.createElementNS("urn:oasis:names:tc:opendocument:xmlns:office:1.0", "office:document");

    // Combine the retrieved xml files from the OpenDocument
    masterDoc.appendChild(root);
    root.appendChild(doc_content.documentElement);
    root.appendChild(doc_style.documentElement);
    root.appendChild(doc_meta.documentElement);
    root.appendChild(doc_settings.documentElement);
    logDebug("odfFileConverter::transform", "XML files combined into one file for transformation.");

//--------------------start v0.2.0-ADD
    //the tempfolder is system dependent, and the name of the document is document dependent,
    //so these have to be injected dynamically into the xsl file
    var imageLocation = gTempDir.path;
    //every replace seems to replace only one '\' char
    while (imageLocation.indexOf("\\") > -1) { var imageLocation = imageLocation.replace('\\','/'); }
    imageLocation = "file:///" + imageLocation + "/";
//--------------------end v0.2.0-ADD

    logDebug("odfFileConverter::transform", "Using the xsl stylesheet to convert the odf document.");
    // Load an instance of the the xslt processor
    var processor = Cc["@mozilla.org/document-transformer;1?type=xslt"].createInstance(Ci.nsIXSLTProcessor);

    // Add the Stylesheet to the processor
    processor.importStylesheet(this.getStylesheet(aInputFile));
    processor.setParameter(null, 'param_baseuri', baseUri);
    processor.setParameter(null, 'param_imgloc', imageLocation);    // v0.2.0-ADD
    processor.setParameter(null, 'file_name', aFileName);

    // perform the conversion
    var transformedDoc = processor.transformToDocument(masterDoc);
    logDebug("odfFileConverter::transform", "Conversion Done!");

    // serialize the data to string
    var serializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"].createInstance(Ci.nsIDOMSerializer);
    var serializedODFData = serializer.serializeToString(transformedDoc);
    logDebug("odfFileConverter::transform", "Data content serialized.");

    logDebug("odfFileConverter::transform", "Transformed Document: \n" + serializedODFData);

    logDebug("odfFileConverter::transform", "----END----");
    return serializedODFData;
}

var oFCLoaded = true;
